/*
 * Copyright (c) 2010-2011 Josef Hardi <josef.hardi@gmail.com>
 * Copyright (c) 2010 Piotr Zagawa <piotr.zagawa@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package id.duapuluhdua.dakidaki.gps;

import android.content.Context;
import android.database.Cursor;
import android.location.Location;

import id.duapuluhdua.dakidaki.data.common.DataValues;
import id.duapuluhdua.dakidaki.data.common.Database;
import id.duapuluhdua.dakidaki.data.tables.DataTableGeoPoints;

/**
 *
 */
public class GpsLogger
{
  private static final String TAG = "GpsLogger";

  /** Constants */
  public final static int MODE_NONE = 0;
  public final static int MODE_UPDATE = 1;
  public final static int MODE_ADD = 2;

  /** Fields */
  private int locationSaveMode = MODE_NONE;

  protected Context mContext;
  protected Database mDatabase;
  
  private Location mOldLocation;
  private GpsLocation mNewGpsLocation;

  private long recordingTrackId = -1;
  private long newPointId = -1;

  private final Boolean bLocationMutex = false;

  /**
   * Creates a GPS logger.
   *
   * @param context
   *            The application context.
   * @param database
   *            The application local database.
   */
  public GpsLogger(Context context, Database database)
  {
    mContext = context;
    mDatabase = database;
    mNewGpsLocation = new GpsLocation();
  }

  public void setRecordingTrackId(long trackId)
  {
    synchronized (bLocationMutex)
    {
      recordingTrackId = trackId;
      mOldLocation = null;
    }
  }

  private long getRecordingTrackId()
  {
    long value = -1;
    synchronized (bLocationMutex)
    {
      value = recordingTrackId;
    }
    return value;
  }

  protected void saveLocation(Location location)
  {
    if (location == null) {
      return;
    }

    /* Filter location and set save mode */
    checkNewLocation(location);

    /* Save location */
    synchronized (bLocationMutex)
    {
      if (mOldLocation != null) {
        if (getRecordingTrackId() != -1) {
          if (mDatabase.isOpen() && mDatabase.getTableGeoPoints().exists()) {
            if (locationSaveMode == MODE_ADD) {
              addLocation();
            }
            if (locationSaveMode == MODE_UPDATE) {
              updateLocation();
            }
          }
        }
      }
    }
  }

  protected void sendNewLocationToParent(Location location)
  {
    /* Send location data with Broadcast intent */
    if (location != null) {
      final long lPointID = (locationSaveMode == MODE_NONE) ? -1 : newPointId;

      mNewGpsLocation.set(location, lPointID, locationSaveMode);
      mNewGpsLocation.send(mContext);
    }
  }

  private void checkNewLocation(Location location)
  {
    if (location == null) {
      return;
    }

    locationSaveMode = MODE_NONE;

    synchronized (bLocationMutex)
    {
      if (mOldLocation == null) {
        mOldLocation = new Location(location);
        locationSaveMode = MODE_ADD;
        return;
      }

      /* Add new location if distance changed */
      if (mOldLocation.distanceTo(location) > location.getAccuracy()) {
        mOldLocation = new Location(location);
        locationSaveMode = MODE_ADD;
      }
      /* Update location if new accuracy is better */
      else if (location.getAccuracy() < mOldLocation.getAccuracy()) {
        mOldLocation = new Location(location);
        locationSaveMode = MODE_UPDATE;
      }
    }
  }

  private DataValues setDataValues(DataValues values)
  {
    final long trackId = getRecordingTrackId();

    synchronized (bLocationMutex)
    {
      values.setValue(DataTableGeoPoints.column.TrackID, trackId);
      values.setValue(DataTableGeoPoints.column.Lon, mOldLocation.getLongitude());
      values.setValue(DataTableGeoPoints.column.Lat, mOldLocation.getLatitude());
      values.setValue(DataTableGeoPoints.column.Altitude, (int)mOldLocation.getAltitude());
      values.setValue(DataTableGeoPoints.column.TimeUTC, mOldLocation.getTime());
      values.setValue(DataTableGeoPoints.column.Accuracy, (int)mOldLocation.getAccuracy());
      values.setValue(DataTableGeoPoints.column.Speed, (int)mOldLocation.getSpeed());
      values.setValue(DataTableGeoPoints.column.Bearing, (int)mOldLocation.getBearing());
      values.setValueCurrentTime(DataTableGeoPoints.column.CreationTime);
    }
    return values;
  }

  private void addLocation()
  {
    final DataTableGeoPoints table = mDatabase.getTableGeoPoints();
    final DataValues values = setDataValues(new DataValues(table));
    newPointId = table.dataInsert(values);
  }

  private void updateLocation()
  {
    final DataTableGeoPoints table = mDatabase.getTableGeoPoints();
    if (newPointId != -1) {
      final Cursor inputCursor = table.getDataRecord(newPointId);
      if (inputCursor != null) {
        final DataValues values = new DataValues(table, inputCursor);
        table.dataUpdate(values, newPointId);
      }
    }
  }
}
